<template>
  <view>
    <view class="process-container" v-if="!loading">
      <view v-for="(task, index) in processTasks">
        <ProcessNodeRender :ref="task.id" :task="task" @addUser="addUser" @delUser="delUser"></ProcessNodeRender>
      </view>
    </view>

    <!-- 选择用户 -->
    <!--		<up-popup mode="right" :customStyle="{width: '100vw', overflowY: 'auto', height: 'auto'}" v-model:show="show">-->
    <!--			<up-navbar :autoBack="false" placeholder @leftClick="closePopup"></up-navbar>-->
    <!--			<OrgPicker ref="mallList" :overlay="false" :entry="'component'" @ok="selected"></OrgPicker>-->
    <!--		</up-popup>-->
  </view>
</template>
<script>
import processApi from '@/apis/cloud/process';
import OrgPicker from "@/components/OrgPicker/index.vue";
import { useUserStore } from '@/pinia-store/user';
import { forEachNode } from '@/utils/ProcessUtil';
import ProcessNodeRender from "./ProcessNodeRender";
export default {
  name: "ProcessRender",
  components: { ProcessNodeRender, OrgPicker },
  props: {
    pcMode: {
      type: Boolean,
      default: true
    },
    processDefId: String,
    process: {
      type: Object,
      default: () => {
        return {}
      }
    },
    formData: {
      type: Object,
      default: () => {
        return {}
      }
    },
    value: {
      type: Object,
      default: () => {
        return {}
      }
    },
    deptId: {
      type: String,
      default: null
    },
    processDataDetail: {
      type: Array,
      default: () => {
        return []
      }
    }
  },
  computed: {
    // 更改为读取用户信息
    loginUser () {
      const user = useUserStore().user;
      user.name = user.worker_name;
      user.avatar = user.img_url;
      return user
    },
    _value: {
      get () {
        // 确保始终返回对象，即使传入的是 falsy 值或字符串
        if (typeof this.value === 'string') {
          return {};
        }
        return this.value || {};
      },
      set (val) {
        // 确保不设置字符串值
        if (typeof val !== 'object' || val === null) {
          this.$emit('input', {});
          return;
        }
        this.$emit('input', val);
      }
    }
  },
  data () {
    return {
      selectUserNodes: new Set(),
      loading: false,
      selectedNode: {},
      reverse: false,
      userCatch: {},
      oldFormData: {},
      models: null,
      processTasks: [],
      conditionFormItem: new Set(),
      branchNodeMap: new Map(),
      loadingReqs: [],
      calls: [],
      show: false
    }
  },
  created () {
    uni.$on('applyProcessRender', (data) => {
      this.selected(data)
    })
  },
  mounted () {
    this.oldFormData = JSON.parse(JSON.stringify(this.formData));

    // 强化本地缓存读取逻辑
    let processData;
    try {
      const storage = uni.getStorageSync("sp");
      processData = (typeof storage === 'object' && storage !== null)
        ? storage
        : {};
    } catch (e) {
      processData = {};
    }

    this._value = processData;

    if (this.process && Object.keys(this.process).length > 0) {
      this.saveProcessToLocalFromTree()
    }
    this.loadProcessRender();
    this.setechodata()
  },
  methods: {
    saveProcessToLocalFromTree () {
      if (!this.process || typeof this.process !== 'object') return

      const processData = {}

      // 递归遍历树
      const traverse = (node) => {
        if (!node || typeof node !== 'object') return

        // 提取当前节点人员
        const nodeId = node.id
        const users = node?.props?.assignedUser || []

        // 如果节点有人员，就保存
        if (nodeId && Array.isArray(users) && users.length > 0) {
          processData[nodeId] = users
        }

        // 继续遍历子节点（children 可能是对象或数组）
        if (node.children) {
          if (Array.isArray(node.children)) {
            node.children.forEach(child => traverse(child))
          } else if (typeof node.children === 'object') {
            traverse(node.children)
          }
        }
      }

      // 从根节点开始
      traverse(this.process)

      // 存入本地缓存
      uni.setStorageSync('sp', processData)
      this._value = processData
    },
    setechodata () {
      if (!this.processDataDetail || Object.keys(this.processDataDetail).length === 0) {
        return;
      }

      // 遍历每个流程节点
      this.processTasks.forEach(task => {
        // 跳过“结束”节点或没有 id 的节点
        if (!task.id || task.name === 'END') {
          return;
        }

        const nodeId = task.id;
        if (this.processDataDetail[nodeId]) {
          // 找到后端回显的人员
          const users = this.processDataDetail[nodeId].map(u => ({
            ...u,
            enableEdit: u.enableEdit ?? true // 确保可编辑
          }));

          // 给 task 设置 users
          this.$set(task, 'users', users);

          // 同步到 _value
          this.$set(this._value, nodeId, users);

          // 同步缓存
          let processData = uni.getStorageSync("sp") || {};
          processData[nodeId] = users;
          uni.setStorageSync("sp", processData);
        }
      });
    },

    closePopup () {
      this.show = false;
      this.$refs.mallList.selection = [];
    },
    async loadProcessRender () {
      this.loading = true
      this.processTasks.length = 0
      this.selectUserNodes.clear()
      this.loadingReqs.length = 0
      //TODO 从这里可以使用去抖动函数 this.$debounce
      this.loadProcess(this.process, this.processTasks)
      this.processTasks.push({
        title: '结束',
        name: 'END',
        icon: 'el-icon-success',
        enableEdit: false
      })
      // console.log('this.processTasks',this.processTasks)
      if (this.loadingReqs.length > 0) {
        Promise.all(this.loadingReqs).then(() => {
          this.loading = false
          this.$emit('render-ok')
        }).catch(() => this.loading = false)
      } else {
        this.$emit('render-ok')
        this.loading = false
      }
    },
    loadProcess (processNode, processTasks, bnode, bid) {
      forEachNode(processNode, (node) => {
        if (bnode) { //如果是分支内子节点
          this.branchNodeMap.set(node.id, { node: bnode, id: bid })
        }
        switch (node.type) {
          case 'ROOT':
            processTasks.push({
              id: node.id,
              title: node.name,
              name: '发起人',
              desc: `${this.loginUser.worker_name} 将发起本流程`,
              icon: 'el-icon-user-solid',
              enableEdit: false,
              users: [this.loginUser]
            });
            break;
          case 'APPROVAL':
            processTasks.push(this.getApprovalNode(node))
            break;
          case 'TASK':
            processTasks.push(this.getApprovalNode(node, false))
            break;
          case 'SUBPROC':
            processTasks.push(this.getSubProcNode(node))
            break;
          case 'CC':
            processTasks.push(this.getCcNode(node))
            break;
          case 'CONDITIONS': //条件节点选一项
            processTasks.push(this.getConditionNode(node, bnode, bid))
            this.loadProcess(node.children, processTasks)
            return true
          case 'INCLUSIVES': //包容分支会执行所有符合条件的分支
            processTasks.push(this.getInclusiveNode(node, bnode, bid))
            this.loadProcess(node.children, processTasks)
            return true
          case 'CONCURRENTS'://并行分支无条件执行所有分支
            processTasks.push(this.getConcurrentNode(node, bnode, bid))
            this.loadProcess(node.children, processTasks)
            return true
        }
      })
    },
    getSubProcNode (node) {
      let user = {}
      //提取发起人
      switch (node.props.staterUser.type) {
        case "ROOT": user = this.loginUser; break;
        case "FORM":
          const fd = this.formData[node.props.staterUser.value]
          user = Array.isArray(fd) && fd.length > 0 ? fd[0] : { name: '请选人' };
          break;
        case "SELECT": user = node.props.staterUser.value || {}; break;
      }
      const procNode = {
        id: node.id,
        title: `${node.name} [由${user.id ? user.name : '?'}发起]`,
        name: '子流程',
        desc: '',
        icon: 'el-icon-money',
        enableEdit: false,
        users: [user]
      }
      this.getSubModel(() => {
        procNode.desc = `调用子流程 [${this.models[node.props.subProcCode]}]`
      })
      return procNode
    },
    getApprovalNode (node, isApproval = true) {
      let result = {
        id: node.id,
        title: node.name,
        name: isApproval ? '审批人' : '办理人',
        icon: isApproval ? 'el-icon-s-check' : 'el-icon-s-claim',
        enableEdit: false,
        multiple: false,
        mode: node.props.mode,
        users: [],
        desc: ''
      }
      let loadCatch = true
      switch (node.props.assignedType) {
        case 'ASSIGN_USER':
          result.users = JSON.parse(JSON.stringify(node.props.assignedUser))
          result.desc = isApproval ? '指定审批人' : '指定办理人'
          break
        case 'ASSIGN_LEADER':
          this.resolveDeptNode(node.props.assignedDept, node.props.deptProp, result, isApproval)
          break
        case 'SELF':
          result.users = [this.loginUser]
          result.desc = `发起人自己${isApproval ? '审批' : '办理'}`
          break
        case 'SELF_SELECT':
          result.enableEdit = true
          this.selectUserNodes.add(node.id)
          result.multiple = node.props.selfSelect.multiple || false
          result.desc = isApproval ? '自选审批人' : '自选办理人'
          break
        case 'LEADER_TOP':
          result.desc = `连续多级主管${isApproval ? '审批' : '办理'}`
          const leaderTop = node.props.leaderTop
          this.loadingReqs.push(
            processApi.getUserLeaders({
              level: 'TOP' === leaderTop.endCondition ? 0 : leaderTop.level,
              deptId: this.deptId,
              empty: leaderTop.skipEmpty,
              token: this.loginUser.token
            }).then(res => {
              result.users = res.data
            }))
          break
        case 'LEADER':
          result.desc = node.props.leader.level === 1 ? `直接主管${isApproval ? '审批' : '办理'}` : `第${node.props.leader.level}级主管${isApproval ? '审批' : '办理'}`
          this.loadingReqs.push(
            processApi.getUserLeader({
              level: node.props.leader.level,
              deptId: this.deptId,
              empty: node.props.leader.skipEmpty,
              token: this.loginUser.token
            }).then(res => {
              result.users = res.data ? [res.data] : [];
            }))
          break
        case 'ROLE':
          result.desc = `由角色[${(node.props.role || []).map(r => r.name)}]${isApproval ? '审批' : '办理'}`
          this.loadingReqs.push(
            processApi.getUsersByRoles({
              role: ((node.props.role || []).map(r => r.id)).join(','),
              token: this.loginUser.token
            }).then(res => {
              result.users = res.data
            }))
          break
        case 'OTHER_SELECT':
          result.desc = `由其他节点指定${isApproval ? '审批人' : '办理人'}`
          break
        case 'FORM_USER':
          loadCatch = false
          result.desc = `由表单字段内人员${isApproval ? '审批' : '办理'}`
          this.conditionFormItem.add(node.props.formUser)
          result.users = this.formData[node.props.formUser] || []
          break
        case 'FORM_DEPT':
          loadCatch = false
          this.conditionFormItem.add(node.props.formDept)
          this.resolveDeptNode(this.formData[node.props.formDept], node.props.deptProp, result, isApproval)
          break
        case 'REFUSE':
          result.desc = `流程此处将被自动驳回`
          break
      }
      if (this.userCatch[node.id] && this.userCatch[node.id].length > 0) {
        result.users = this.userCatch[node.id]
      }
      if (loadCatch) {
        this.userCatch[node.id] = result.users
      }
      return result
    },
    resolveDeptNode (depts, deptProp, result, isApproval) {
      let deptIds = (depts || []).map(d => d.id)
      switch (deptProp?.type) {
        case 'ALL':
          result.desc = `部门内所有人${isApproval ? '审批' : '办理'}`;
          result.users = depts
          break;
        case 'ROLE':
          result.desc = `部门内角色[${(deptProp.roles || []).map(r => r.name)}]${isApproval ? '审批' : '办理'}`
          this.loadingReqs.push(
            processApi.getDeptUsersByRoles((deptProp.roles || []).map(r => r.id), deptIds).then(res => {
              result.users = res.data
            }))
          break;
        default:
          this.loadingReqs.push(
            processApi.getLeaderByDepts(deptIds).then(res => {
              result.users = res.data
            }))
          result.desc = `部门负责人${isApproval ? '审批' : '办理'}`;
          break;
      }
    },
    getCcNode (node) {
      let result = {
        id: node.id,
        title: node.name,
        icon: 'el-icon-s-promotion',
        name: '抄送人',
        enableEdit: node.props.shouldAdd,
        type: 'org',
        multiple: true,
        desc: node.props.shouldAdd ? '可添加抄送人' : '流程将会抄送到他们',
        users: JSON.parse(JSON.stringify(node.props.assignedUser))
      }
      if (this.userCatch[node.id] && this.userCatch[node.id].length > 0) {
        result.users = this.userCatch[node.id]
      }
      this.userCatch[node.id] = result.users
      return result
    },
    getInclusiveNode (node, pbnode, pbid) {
      let branchTasks = {
        id: node.id,
        title: node.name,
        name: '包容分支',
        icon: 'el-icon-connection',
        enableEdit: false,
        active: node.branchs[0].id, //激活得分支
        options: [], //分支选项，渲染单选框
        desc: '满足条件的分支均会执行',
        branchs: {} //分支数据，不包含分支节点，key=分支子节点id，value = [后续节点]
      }
      const req = processApi.getTrueConditions({
        processDfId: this.processDefId,
        conditionNodeId: node.id,
        multiple: true,
        context: { ...this.formData, deptId: this.deptId },
        token: this.loginUser.token
      }).then(rsp => {
        //拿到满足的条件
        const cds = new Set(rsp.data || [])
        for (let i = 0; i < node.branchs.length; i++) {
          const cdNode = node.branchs[i]
          this.$set(cdNode, 'skip', !cds.has(cdNode.id))
          if (!cdNode.skip) {
            branchTasks.active = cdNode.id
          }
        }
        node.branchs.forEach(nd => {
          branchTasks.options.push({
            id: nd.id,
            title: nd.name,
            skip: nd.skip
          })
          this.$set(branchTasks.branchs, nd.id, [])
          //设置下子级分支的父级分支节点
          this.branchNodeMap.set(nd.id, { node: pbnode, id: pbid })
          this.loadProcess(nd.children, branchTasks.branchs[nd.id], branchTasks, nd.id)
        })
      }).catch(err => {
        branchTasks.desc = `<span style="color:#CE5266;">条件解析异常，渲染失败😢<span>`
        this.$err(err, "解析条件失败:")
      })
      this.loadingReqs.push(req)
      return branchTasks
    },
    getConditionNode (node, pbnode, pbid) {
      let branchTasks = {
        id: node.id,
        title: node.name,
        name: '条件分支',
        icon: 'el-icon-share',
        enableEdit: false,
        active: node.branchs[0].id, //激活得分支
        options: [], //分支选项，渲染单选框
        desc: '只执行第一个满足条件的分支',
        branchs: {} //分支数据，不包含分支节点，key=分支子节点id，value = [后续节点]
      }
      const req = processApi.getTrueConditions({
        processDfId: this.processDefId,
        conditionNodeId: node.id,
        multiple: false,
        context: { ...this.formData, deptId: this.deptId },
        token: this.loginUser.token
      }).then(rsp => {
        //拿到满足的条件
        const cds = new Set(rsp.data || [])
        for (let i = 0; i < node.branchs.length; i++) {
          const cdNode = node.branchs[i]
          this.$set(cdNode, 'skip', !cds.has(cdNode.id))
          if (!cdNode.skip) {
            branchTasks.active = cdNode.id
          }
        }
        node.branchs.forEach(nd => {
          branchTasks.options.push({
            id: nd.id,
            title: nd.name,
            skip: nd.skip
          })
          this.$set(branchTasks.branchs, nd.id, [])
          //设置下子级分支的父级分支节点
          this.branchNodeMap.set(nd.id, { node: pbnode, id: pbid })
          this.loadProcess(nd.children, branchTasks.branchs[nd.id], branchTasks, nd.id)
        })
      }).catch(err => {
        branchTasks.desc = `<span style="color:#CE5266;">条件解析异常，渲染失败😢<span>`
        this.$err(err, "解析条件失败:")
      })
      this.loadingReqs.push(req)
      return branchTasks
    },
    getConcurrentNode (node, pbnode, pbid) {
      let concurrentTasks = {
        id: node.id,
        title: node.name,
        name: '并行分支',
        icon: 'el-icon-s-operation',
        enableEdit: false,
        active: node.branchs[0].id, //激活得分支
        options: [], //分支选项，渲染单选框
        desc: '所有分支都将同时执行',
        branchs: {} //分支数据，不包含分支节点，key=分支子节点id，value = [后续节点]
      }
      node.branchs.forEach(nd => {
        concurrentTasks.options.push({
          id: nd.id,
          title: nd.name,
          skip: false
        })
        this.$set(concurrentTasks.branchs, nd.id, [])
        //设置下子级分支的父级分支节点
        this.branchNodeMap.set(nd.id, { node: pbnode, id: pbid })
        this.loadProcess(nd.children, concurrentTasks.branchs[nd.id], concurrentTasks, nd.id)
      })
      return concurrentTasks
    },
    selected (users) {
      if (!this.selectedNode || !Array.isArray(this.selectedNode.users)) {
        this.$set(this.selectedNode, 'users', []);
      }

      users.forEach(u => {
        if (!this.selectedNode.users.some(v => v.id === u.id)) {
          this.selectedNode.users.push({ ...u, enableEdit: true });
        }
      });

      const updatedUsers = this.uniqById(this.selectedNode.users);
      this._value[this.selectedNode.id] = updatedUsers;

      let processData = uni.getStorageSync("sp") || {};
      processData[this.selectedNode.id] = updatedUsers;
      processData = this.cleanObject(processData);
      uni.setStorageSync("sp", processData);

      this.show = false;
    }
    ,
    //作废
    delUser (nodeId, index, user) {
      if (!Array.isArray(this._value[nodeId])) return;

      this._value[nodeId].splice(index, 1);
      this._value[nodeId] = this.uniqById(this._value[nodeId]);

      let processData = uni.getStorageSync("sp") || {};
      if (this._value[nodeId].length > 0) {
        processData[nodeId] = this._value[nodeId];
      } else {
        delete processData[nodeId];
      }
      processData = this.cleanObject(processData);
      uni.setStorageSync("sp", processData);
    },
    getSubModel (call) {
      if (this.models) {
        call()
      } else {
        this.calls.push(call)
        if (this.calls.length === 1) {
          processApi.getGroupModels({}, true).then(rsp => {
            this.models = {}
            rsp.data.forEach(group => {
              group.items.forEach(v => this.$set(this.models, v.procCode, v.procName))
            })
            this.calls.forEach(callFun => callFun())
            this.calls.length = 0
          })
        }
      }
    },
    // 选择用户抽屉
    addUser (node) {
      if (!node.users) {
        this.$set(node, 'users', []);
      }
      this.selectedNode = node;
      uni.navigateTo({
        url: '/components/OrgPicker/index?overlay=overlay&entry=component&VisibleandHidden=true&title=人员选择&source=ProcessRender'
      });
      // this.show = true;
      // console.log(node);
    },
    getApprovalMode (mode) {
      switch (mode) {
        case 'AND':
          return '会签';
        case 'NEXT':
          return '按顺序会签'
        case 'OR':
          return '或签'
        default:
          return '?'
      }
    },
    async formChangeHandler (newVal, oldVal) {
      let isChanged = false
      for (const key of this.conditionFormItem) {
        if (this.formValDiff(newVal[key], oldVal[key])) {
          isChanged = true
          break
        }
      }
      if (isChanged) {
        console.log('执行流程重渲染')
        this.loadProcessRender()
      }
      this.oldFormData = JSON.parse(JSON.stringify(this.formData))
    },
    formValDiff (newVal, oldVal) {
      if (typeof (newVal) === 'object') {
        return JSON.stringify(newVal) !== JSON.stringify(oldVal)
      } else {
        return newVal !== oldVal
      }
    },
    //执行校验流程步骤设置
    validate (call) {
      //遍历自选审批人节点
      let isOk = true
      for (let nodeId of this.selectUserNodes) {
        if ((this._value[nodeId] || []).length === 0) {
          //没设置审批人员
          isOk = false
          //遍历所有的分支，从底部向上搜索进行自动切换分支渲染路线
          let brNode = this.branchNodeMap.get(nodeId)
          while (brNode && brNode.id) {
            brNode.node.active = brNode.id
            brNode = this.branchNodeMap.get(brNode.id)
          }
          this.$nextTick(() => {
            if (this.$refs[nodeId]) {
              this.$refs[nodeId].errorShark()
            }
          })
          break
        }
      }
      if (call) {
        call(isOk)
      }
    },
    uniqById (arr) {
      const map = new Map();
      (arr || []).forEach(u => {
        if (u && u.id) {
          map.set(u.id, u);
        }
      });
      return Array.from(map.values());
    },
    cleanObject (obj) {
      const newObj = {};
      Object.keys(obj || {}).forEach(k => {
        if (k && k !== 'undefined') {
          newObj[k] = obj[k];
        }
      });
      return newObj;
    }
  },
  watch: {
    formData: {
      deep: true,
      handler (val) {
      }
    }
  }
}
</script>

<style lang="less" scoped>
.process-container {
  //margin: 0 auto;
  //width: 91%;
  padding: 20rpx;
  margin-top: 20rpx;
  background-color: #fff;
  //border-radius: 16rpx;
  //box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
}
</style>
